package com.shycloud.mido.auth.config;

import cn.hutool.core.util.StrUtil;

import com.shycloud.mido.auth.tokenService.CustomTokenServices;
import com.shycloud.mido.common.core.constant.SecurityConstants;
import com.shycloud.mido.common.data.tenant.TenantContextHolder;
import com.shycloud.mido.common.security.component.ShyWebResponseExceptionTranslator;
import com.shycloud.mido.common.security.service.*;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 认证服务器配置
 *
 * @author nianhua.jiang
 * @date 2020/6/12 10:24
 */
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	private final DataSource dataSource;

	private final ShyUserDetailsService shyUserDetailsService;

	private final AuthenticationManager authenticationManagerBean;

	private final RedisConnectionFactory redisConnectionFactory;

	@Override
	@SneakyThrows
	public void configure(ClientDetailsServiceConfigurer clients) {
		ShyClientDetailsService clientDetailsService = new ShyClientDetailsService(dataSource);
		clientDetailsService.setSelectClientDetailsSql(SecurityConstants.DEFAULT_SELECT_STATEMENT);
		clientDetailsService.setFindClientDetailsSql(SecurityConstants.DEFAULT_FIND_STATEMENT);
		clients.withClientDetails(clientDetailsService);
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
		oauthServer
				.allowFormAuthenticationForClients()
				.checkTokenAccess("isAuthenticated()");
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

		endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
				.tokenStore(tokenStore())
				.tokenEnhancer(tokenEnhancer())
				.userDetailsService(shyUserDetailsService)
				.authenticationManager(authenticationManagerBean)
				.reuseRefreshTokens(false)
				.tokenServices(tokenServices())
				.exceptionTranslator(new ShyWebResponseExceptionTranslator());

	}


	@Bean
	public TokenStore tokenStore() {

		RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
		tokenStore.setPrefix(SecurityConstants.SHYCLOUD_PREFIX + SecurityConstants.OAUTH_PREFIX);
		tokenStore.setAuthenticationKeyGenerator(new DefaultAuthenticationKeyGenerator() {
			@Override
			public String extractKey(OAuth2Authentication authentication) {
				return super.extractKey(authentication) + ":" + TenantContextHolder.getTenantId();
			}
		});
		return tokenStore;

	}

	/**
	 * token增强，客户端模式不增强。
	 *
	 * @return TokenEnhancer
	 */
	@Bean
	public TokenEnhancer tokenEnhancer() {
		return (accessToken, authentication) -> {
			if (SecurityConstants.CLIENT_CREDENTIALS
					.equals(authentication.getOAuth2Request().getGrantType())) {
				return accessToken;
			}

			final Map<String, Object> additionalInfo = new HashMap<>();
			User user = (User) authentication.getUserAuthentication().getPrincipal();

			//================================WX/手机端登录 START=========================================//
			final Map<String, Object> additionalInfowx = new HashMap<>();
			if (StrUtil.startWithAny(user.getUsername(), "mobile@@")) {
				final Map<String, Object> resultMap = new HashMap<>();
				ShyMember member = (ShyMember) authentication.getUserAuthentication().getPrincipal();

				if ("1".equals(member.getLoginType())) {
					additionalInfo.put("org_id", member.getOrgId());
					additionalInfo.put("org_name", member.getOrgName());
					additionalInfo.put("sex", member.getSex());
					additionalInfo.put("birthday", member.getBirthday());
					additionalInfo.put("isTeacher", member.getIsTeacher());
					additionalInfo.put("user_id", member.getId());
					additionalInfo.put("showName", member.getShowName());
					additionalInfo.put("head", member.getHeader());
					additionalInfo.put("username", member.getLoginname());
					additionalInfo.put("phone", member.getPhone());
					additionalInfo.put("account", member.getAccount());
					((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
					return accessToken;
				}

				if ("2".equals(member.getLoginType())) {
					additionalInfowx.put("code", 0);
					additionalInfowx.put("msg", "success");
					resultMap.put("org_id", member.getOrgId());
					resultMap.put("org_name", member.getOrgName());
					resultMap.put("sex", member.getSex());
					resultMap.put("birthday", member.getBirthday());
					resultMap.put("isTeacher", member.getIsTeacher());
					resultMap.put("user_id", member.getId());
					resultMap.put("showName", member.getShowName());
					resultMap.put("head", member.getHeader());
					resultMap.put("username", member.getLoginname());
					resultMap.put("phone", member.getPhone());
					resultMap.put("account", member.getAccount());
					resultMap.put("access_token", accessToken.toString());
					resultMap.put("token_type", "bearer");
					additionalInfowx.put("data", resultMap);
					((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfowx);
					return accessToken;
				}

			}
			//================================WX登录 E N D=========================================//

			//================================小程序登录 START=========================================//
			final Map<String, Object> miniprogram = new HashMap<>();
			if (StrUtil.startWithAny(user.getUsername(), "mini@@")) {
				ShyMember member = (ShyMember) authentication.getUserAuthentication().getPrincipal();
				miniprogram.put("code", 0);
				miniprogram.put("msg", "success");
				miniprogram.put("org_id", member.getOrgId());
				miniprogram.put("org_name", member.getOrgName());
				miniprogram.put("sex", member.getSex());
				miniprogram.put("birthday", member.getBirthday());
				miniprogram.put("isTeacher", member.getIsTeacher());
				miniprogram.put("user_id", member.getId());
				miniprogram.put("showName", member.getShowName());
				miniprogram.put("head", member.getHeader());
				miniprogram.put("username", member.getLoginname());
				miniprogram.put("phone", member.getPhone());
				miniprogram.put("wxFlg", member.getWxFlg());
				miniprogram.put("account", member.getAccount());
				miniprogram.put("access_token", accessToken.toString());
				miniprogram.put("token_type", "bearer");
				miniprogram.put("wxOpenId", member.getWxOpenId());
				((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(miniprogram);
				return accessToken;
			}
			//================================WX登录 E N D=========================================//

			//================================机构登录 START=========================================//
			if (StrUtil.startWithAny(user.getUsername(), "organ@@")) {
				ShyOrganMember member = (ShyOrganMember) authentication.getUserAuthentication().getPrincipal();
				additionalInfo.put("user_id", member.getId());
				additionalInfo.put("organ_id", member.getOrganId());
				additionalInfo.put("type", member.getType());
				((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
				return accessToken;
			}
			//================================机构登录 END=========================================//

			//================================总部后台登录 START===================================//
			ShyUser shyUser = (ShyUser) authentication.getUserAuthentication().getPrincipal();
			additionalInfo.put("user_id", shyUser.getId());
			additionalInfo.put("username", shyUser.getUsername());
			additionalInfo.put("dept_id", shyUser.getDeptId());
			additionalInfo.put("role_id", 100);
			additionalInfo.put("tenant_id", shyUser.getTenantId());
			additionalInfo.put("license", SecurityConstants.SHYCLOUD_LICENSE);
			((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
			return accessToken;
			//================================总部后台登录 E N D===================================//
		};
	}

	@Primary
	@Bean
	public AuthorizationServerTokenServices tokenServices() {
		//此处需要重写 defaultTokenService 以实现踢人模式
		CustomTokenServices defaultTokenServices = new CustomTokenServices();
		//token的过期时间
		defaultTokenServices.setAccessTokenValiditySeconds(604800);
		defaultTokenServices.setSupportRefreshToken(true);
		defaultTokenServices.setReuseRefreshToken(false);
		defaultTokenServices.setTokenStore(tokenStore());
		defaultTokenServices.setTokenEnhancer(tokenEnhancer());
		return defaultTokenServices;
	}
}
